package com.ecarx.vhaltests.hy11.testcases;

import static androidx.test.core.app.ApplicationProvider.getApplicationContext;

import android.car.VehicleAreaType;
import android.car.VehicleLightSwitch;
import android.car.VehiclePropertyIds;
import android.car.hardware.CarPropertyValue;
import android.car.hardware.property.CarPropertyManager;
import android.util.Log;

import com.ecarx.vhaltests.IpcpClient;
import com.ecarx.vhaltests.VehicleConstants;
import com.ecarx.vhaltests.VhalCallBackLock;
import com.ecarx.vhaltests.VhalUnitTestBase;
import com.ecarx.xui.adaptapi.car.IWrapper;
import com.ecarx.xui.adaptapi.car.vehicle.IBcm;
import com.ecarx.xui.adaptapi.car.vehicle.ILamp;
import com.ecarx.xui.adaptapi.car.vehicle.IVehicle;

import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;

public class FogLightUnitTest extends VhalUnitTestBase {
    private final static String TAG = "FogLightUnitTest";
    private static IWrapper mWrapper;
    private static int mFogLightStatusPropertyId = VehiclePropertyIds.FOG_LIGHTS_STATE;
    private static int mFogLightSwitchPropertyId = VehiclePropertyIds.FOG_LIGHTS_SWITCH;
    private static IWrapper.IPropertyId mLampExteriorLightControlPropertyId;

    static final VhalCallBackLock mLock = new VhalCallBackLock();
    private static Object mHalValue = null;
    private static int mHalStatus = 0;

    public class LiExtFctReq1 {
        public static final int OFF = 0;
        public static final int POS = 1;
        public static final int LO = 2;
        public static final int AUTLI = 3;
    };

    @BeforeClass
    public static void doBefore() {
        doCallbackRegister();
    }

    @AfterClass
    public static void doAfter() {
        doCallbackUnRegister();
    }

    @Test
    public void setFogLampsControlActive() {
        doCarConfigPresetsSet(0);

        String[] cems = { "vehModMngtGlbSafe1CarModSts1", "vehModMngtGlbSafe1UsgModSts", "swtExtrLi2LiExtFctReq1"};
        int[] cemValuesAct = { VehicleConstants.CAR_MODE_NOMAL, VehicleConstants.USG_MODE_ACTIVE, LiExtFctReq1.OFF};
        int[] cemValuesDri = { VehicleConstants.CAR_MODE_NOMAL, VehicleConstants.USG_MODE_DRIVING, LiExtFctReq1.OFF};

        doCEMSignalPresetsSet(cems, cemValuesAct);
        setCDM(LiExtFctReq1.LO);
        int propertyValue = mLampExteriorLightControlPropertyId.getPropertyValue(
                IVehicle.LAMP_EXTERIOR_LIGHT_CONTROL_AUTOMATIC);
        doLampExteriorLightControlSet(propertyValue, propertyValue);

        // make RearFogLight active
        doCEMSignalPresetsSet(cems, cemValuesDri);

        int areaId = VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
        int propertyId = mFogLightSwitchPropertyId;
        checkPropertyAvailable(areaId, propertyId, true);

        checkPropertyAvailable(areaId, mFogLightStatusPropertyId, true);

        testFogLightSetPropGetProp(areaId, propertyId, VehicleLightSwitch.ON, VehicleLightSwitch.ON);
        testFogLightSetPropGetProp(areaId, propertyId, VehicleLightSwitch.OFF, VehicleLightSwitch.OFF);
    }

    @Test
    public void setFogLampsControlNotActive() {
        doCarConfigPresetsSet(0);

        String[] cems = { "vehModMngtGlbSafe1CarModSts1", "vehModMngtGlbSafe1UsgModSts", "swtExtrLi2LiExtFctReq1"};
        int[] cemValuesAct = { VehicleConstants.CAR_MODE_NOMAL, VehicleConstants.USG_MODE_ACTIVE, LiExtFctReq1.OFF};
        int[] cemValuesNotActive = { VehicleConstants.CAR_MODE_NOMAL, VehicleConstants.USG_MODE_DRIVING,
                LiExtFctReq1.OFF};

        doCEMSignalPresetsSet(cems, cemValuesAct);
        setCDM(LiExtFctReq1.LO);
        int propertyValue = mLampExteriorLightControlPropertyId.getPropertyValue(
                IVehicle.LAMP_EXTERIOR_LIGHT_CONTROL_OFF);
        doLampExteriorLightControlSet(propertyValue, propertyValue);
        setCDM(LiExtFctReq1.OFF);
        // make RearFogLight not active
        doCEMSignalPresetsSet(cems, cemValuesNotActive);

        int areaId = VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
        int propertyId = mFogLightSwitchPropertyId;
        checkPropertyAvailable(areaId, propertyId, false);

        checkPropertyAvailable(areaId, mFogLightStatusPropertyId, false);
    }

    @Test
    public void setFogLampsControlActiveWhenCarConfigMatch() {
        doCarConfigPresetsSet(0);

        String[] cems = { "vehModMngtGlbSafe1CarModSts1", "vehModMngtGlbSafe1UsgModSts", "swtExtrLi2LiExtFctReq1" };
        int[] cemValuesAct = { VehicleConstants.CAR_MODE_NOMAL, VehicleConstants.USG_MODE_ACTIVE, LiExtFctReq1.LO };
        // RearFogLight is active when UsgMode == Convenience(Only for CC#13 == 0x04)
        int[] cemValuesConv = { VehicleConstants.CAR_MODE_NOMAL, VehicleConstants.USG_MODE_CONVENIENCE,
                LiExtFctReq1.LO, LiExtFctReq1.OFF  };
        setCDM(LiExtFctReq1.OFF);
        doCEMSignalPresetsSet(cems, cemValuesAct);

        int propertyValue = mLampExteriorLightControlPropertyId.getPropertyValue(
                IVehicle.LAMP_EXTERIOR_LIGHT_CONTROL_AUTOMATIC);
        doLampExteriorLightControlSet(propertyValue, propertyValue);

        // make RearFogLight active
        doCEMSignalPresetsSet(cems, cemValuesConv);

        int areaId = VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
        int propertyId = mFogLightSwitchPropertyId;
        checkPropertyAvailable(areaId, propertyId, true);

        checkPropertyAvailable(areaId, mFogLightStatusPropertyId, true);

        testFogLightSetPropGetProp(areaId, propertyId, VehicleLightSwitch.ON, VehicleLightSwitch.ON);
        testFogLightSetPropGetProp(areaId, propertyId, VehicleLightSwitch.OFF, VehicleLightSwitch.OFF);
    }

    @Test
    public void setFogLampsControlNotActiveWhenCarConfigNotMatch() {
        doCarConfigPresetsSet(1);

        String[] cems = { "vehModMngtGlbSafe1CarModSts1", "vehModMngtGlbSafe1UsgModSts", "swtExtrLi2LiExtFctReq1" };
        int[] cemValuesAct = { VehicleConstants.CAR_MODE_NOMAL, VehicleConstants.USG_MODE_ACTIVE, LiExtFctReq1.LO };
        // RearFogLight is active when UsgMode == Convenience(Only for CC#13 == 0x04)
        int[] cemValuesConv = { VehicleConstants.CAR_MODE_NOMAL, VehicleConstants.USG_MODE_CONVENIENCE,
                LiExtFctReq1.LO };

        doCEMSignalPresetsSet(cems, cemValuesAct);
        setCDM(LiExtFctReq1.OFF);
        int propertyValue = mLampExteriorLightControlPropertyId.getPropertyValue(
                IVehicle.LAMP_EXTERIOR_LIGHT_CONTROL_AUTOMATIC);
        doLampExteriorLightControlSet(propertyValue, propertyValue);

        // RearFogLight not active
        doCEMSignalPresetsSet(cems, cemValuesConv);

        int areaId = VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
        int propertyId = mFogLightSwitchPropertyId;
        checkPropertyAvailable(areaId, propertyId, false);

        checkPropertyAvailable(areaId, mFogLightStatusPropertyId, false);
    }

    private void testFogLightSetPropGetProp(int areaId, int propertyId, int propertyValue, int expectValue) {
        setFogLampsControlPrep(areaId, propertyId, propertyValue);
        setFogLampsControl(areaId, propertyId, propertyValue, expectValue);
    }

    // to make sure: current property value different with target property value.
    private void setFogLampsControlPrep(int areaId, int propertyId, int targetPropValue) {
        int currentPropValue = mCarPropertyMgr.getIntProperty(propertyId, areaId);
        // if same, do nothing here:
        if (targetPropValue != currentPropValue) {
            return;
        }

        int propertyValue = VehicleLightSwitch.OFF;
        if (currentPropValue == VehicleLightSwitch.OFF) {
            propertyValue = VehicleLightSwitch.ON;
        }

        mCarPropertyMgr.setIntProperty(propertyId, areaId, propertyValue);
        delay();
        boolean getPropertyValue = mCarPropertyMgr.getBooleanProperty(propertyId, areaId);
        Log.d(TAG, "setFogLampsControlPrep() getPropertyValue = " + getPropertyValue);
        Assert.assertEquals(propertyValue, getPropertyValue);
    }

    private void doLampExteriorLightControlSet(int propertyValue, int expectValue) {
        final IWrapper.IPropertyId propertyId = mLampExteriorLightControlPropertyId;
        int areaId = VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
        int vhalPropId = propertyId.getPropertyId();
        Log.d(TAG,
                "ExteriorLight AdaptAPI_PropertyId: "
                        + ILamp.SETTING_FUNC_LAMP_EXTERIOR_LIGHT_CONTROL
                        + ", get VHAL_PropertyId = " + vhalPropId);

        boolean isPropertyAvailable = mCarPropertyMgr.isPropertyAvailable(vhalPropId, areaId);
        Log.d(TAG, "ExteriorLight isPropertyAvailable: " + isPropertyAvailable);

        mCarPropertyMgr.setIntProperty(vhalPropId, areaId, propertyValue);
        delay();
        Log.d(TAG, "ExteriorLight setProperty: " + propertyValue);

        int getValue = mCarPropertyMgr.getIntProperty(vhalPropId, areaId);
        Log.d(TAG, "ExteriorLight getProperty: " + getValue);
        Assert.assertEquals(expectValue, getValue);
    }

    private void setFogLampsControl(int areaId, int propertyId, int propertyValue, int expectValue) {
        Log.d(TAG, "FogLight AdaptAPI_PropertyId = " + IBcm.BCM_FUNC_LIGHT_REAR_FOG_LAMPS
                + ", get VHAL_PropertyId = " + propertyId + ", propertyValue = " + propertyValue);
        boolean isPropertyAvailable = mCarPropertyMgr.isPropertyAvailable(propertyId, areaId);
        Log.d(TAG, "FogLight isPropertyAvailable: " + isPropertyAvailable);

        mCarPropertyMgr.setIntProperty(propertyId, areaId, propertyValue);
        Log.d(TAG, "FogLight: setProperty: " + propertyValue);

        try {
            mLock.setPropIdAndAreaId(propertyId, areaId);
            synchronized (mLock) {
                mLock.wait(1000);
            }
            mLock.reSetPropIdAndAreaId();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Assert.assertEquals(mHalStatus, 0);
        Assert.assertNotNull(mHalValue);
        Assert.assertEquals((int) mHalValue, expectValue);
        mHalValue = null;
        mHalStatus = 0;

        int switchValue = mCarPropertyMgr.getIntProperty(propertyId, areaId);
        Log.d(TAG, "FogLightSwitch: getProperty: " + switchValue);

        delay();

        Assert.assertEquals(expectValue, switchValue);

        int stateValue = mCarPropertyMgr.getIntProperty(mFogLightStatusPropertyId, areaId);
        Log.d(TAG, "FogLightState: getProperty: " + stateValue);

        Assert.assertEquals(expectValue, stateValue);
    }

    private void checkPropertyAvailable(int areaId, int propertyId, boolean expectPropertyStatus) {
        Log.d(TAG, "checkPropertyAvailable() areaId = " + areaId + ",propertyId = " + propertyId
                + ", expectPropertyStatus = " + expectPropertyStatus);
        boolean currentPropertyStatus = mCarPropertyMgr.isPropertyAvailable(propertyId, areaId);
        Log.d(TAG, "checkPropertyAvailable() areaId = "
                + areaId + ", propertyId = " + propertyId + ", currentPropertyStatus = " + currentPropertyStatus);
        Assert.assertEquals(currentPropertyStatus, expectPropertyStatus);
    }

    private boolean doCarConfigPresetsSet(int mode) {
        Log.d(TAG, "doPresetsConfigSet()");
        IpcpClient ipcpClient = new IpcpClient();

        int ccSvcId = 135;
        int ccOperId = 200;
        int[] ccOperIds = { 200 };
        ipcpClient.setConfig(ccSvcId, ccOperIds);
        boolean setCarconfigSignalStatus;

        if (mode == 0) {
            int[] configs = { 118, 13, 98, 463 };
            int[] values = { 129, 4, 2, 2 };
            setCarconfigSignalStatus = ipcpClient.setCarconfigSignal(ccSvcId, ccOperId, configs, values);
        } else {
            int[] configs = { 118, 98, 463 };
            int[] values = { 129, 2, 2 };
            setCarconfigSignalStatus = ipcpClient.setCarconfigSignal(ccSvcId, ccOperId, configs, values);
        }

        Log.d(TAG, "doPresetsConfigSet() setCarconfigSignalStatus = " + setCarconfigSignalStatus);

        delay();

        return setCarconfigSignalStatus;
    }

    private void doVGMSignalPresetsSet(String[] vgms, int[] vgmValues) {
        Log.d(TAG, "doPresetsConfigSet()");
        IpcpClient ipcpClient = new IpcpClient();
        int vgmSvcId = 49; // EcarXSignalServiceID::serviceID_VGM (49)

        int vgmOperId = 999;
        int[] vgmOperIds = { 999 };
        int ccSvcId = 135;
        int[] ccOperIds = { 200 };
        ipcpClient.setConfig(vgmSvcId, vgmOperIds);
        ipcpClient.setConfig(ccSvcId, ccOperIds);

        boolean setVGMSignalStatus = ipcpClient.setVGMSignal(vgmSvcId, vgmOperId, vgms, vgmValues);
        Log.d(TAG, "doPresetsConfigSet() setVGMSignalStatus = " + setVGMSignalStatus);
        delay();
    }

    private void doCEMSignalPresetsSet(String[] cems, int[] cemValues) {
        Log.d(TAG, "doCEMSignalPresetsSet()");
        IpcpClient ipcpClient = new IpcpClient();
        int cemSvcId = 37; // EcarXSignalServiceID::serviceID_CEM (37)

        int cemOperId = 999;
        int[] vgmOperIds = { 999 };
        int ccSvcId = 135;
        int[] ccOperIds = { 200 };
        ipcpClient.setConfig(cemSvcId, vgmOperIds);
        ipcpClient.setConfig(ccSvcId, ccOperIds);

        boolean setCEMSignalStatus = ipcpClient.setCEMSignal(cemSvcId, cemOperId, cems, cemValues);
        Log.d(TAG, "doCEMSignalPresetsSet() setCEMSignalStatus = " + setCEMSignalStatus);

        delay();
    }

    private static void doCallbackRegister() {
        mWrapper = com.ecarx.xui.adaptapi.car.Car.createWrapper(getApplicationContext());
        mLampExteriorLightControlPropertyId = mWrapper.getWrappedPropertyId(IWrapper.WrappedIdType.ID_TYPE_FUNCTION,
                ILamp.SETTING_FUNC_LAMP_EXTERIOR_LIGHT_CONTROL);
        mCarPropertyMgr.registerCallback(mPropertyEventCallback,
                mFogLightStatusPropertyId, CarPropertyManager.SENSOR_RATE_ONCHANGE);
        mCarPropertyMgr.registerCallback(mPropertyEventCallback,
                mFogLightSwitchPropertyId, CarPropertyManager.SENSOR_RATE_ONCHANGE);
    }

    private static void doCallbackUnRegister() {
        mCarPropertyMgr.unregisterCallback(mPropertyEventCallback, mFogLightStatusPropertyId);
        mCarPropertyMgr.unregisterCallback(mPropertyEventCallback, mFogLightSwitchPropertyId);
    }

    private static CarPropertyManager.CarPropertyEventCallback mPropertyEventCallback = new CarPropertyManager.CarPropertyEventCallback() {
        @Override
        public void onChangeEvent(CarPropertyValue carPropertyValue) {
            try {
                Log.d(TAG, String.format("onChangeEvent propertyId = %d, areaId = %d, value = %s, status = %d",
                        carPropertyValue.getPropertyId(), carPropertyValue.getAreaId(),
                        carPropertyValue.getValue(), carPropertyValue.getStatus()));
                if (mLock.getPropId() == carPropertyValue.getPropertyId()
                        && mLock.getAreaId() == carPropertyValue.getAreaId()) {
                    mHalStatus = carPropertyValue.getStatus();
                    mHalValue = carPropertyValue.getValue();
                    synchronized (mLock) {
                        mLock.notify();
                    }
                }
            } catch (Exception e) {
                Log.e(TAG, e.getMessage());
            }
        }

        @Override
        public void onErrorEvent(int i, int i1) {
        }
    };


    public void setCDM(int swtExtrLi3LiExtFctReq1) {
        IpcpClient ipcpClient = new IpcpClient();
        int cdmSvcId = 36;
        int[] cdmOperIds = {999};
        int cdmOperId = 999;
        ipcpClient.setConfig(cdmSvcId, cdmOperIds);
        String[] cdms = {"swtExtrLi3LiExtFctReq1"};
        int[] cdmValues = {swtExtrLi3LiExtFctReq1};
        boolean cdmResult = ipcpClient.setCDMSignal(cdmSvcId, cdmOperId, cdms, cdmValues);
        Assert.assertTrue(cdmResult);
        delay();
    }
}
